package com.vteba.shards.schema.service;

import java.sql.SQLException;
import java.util.Map;

import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import com.alibaba.druid.pool.DruidDataSource;
import com.vteba.service.context.spring.ApplicationContextHolder;
import com.vteba.shards.schema.model.SchemaInfo;
import com.vteba.tx.jdbc.datasource.MultiDataSourceTransactionManager;
import com.vteba.tx.jdbc.mybatis.session.SqlSessionFactoryBean;
import com.vteba.tx.jdbc.mybatis.session.SqlSessionTemplate;
import com.vteba.utils.common.Assert;

public class SchemaInfoService implements InitializingBean {
	private static final Logger LOGGER = LogManager.getLogger(SchemaInfoService.class);
	
	private static final String SQL_SESSION_FACTORY = "SqlSessionFactory";
	
	private SqlSessionTemplate sqlSessionTemplateProxy;
	
	private MultiDataSourceTransactionManager chainedTransactionManager;
	
	public boolean createSchema(SchemaInfo schemaInfo) {
		String beanName = schemaInfo.getPeerName() + SQL_SESSION_FACTORY;
		SqlSessionFactoryBean skmbwSqlSessionFactory = ApplicationContextHolder.getFactoryBean(beanName);
		
		DruidDataSource peerDataSource = (DruidDataSource) skmbwSqlSessionFactory.getDataSource();
		DruidDataSource dataSource = peerDataSource.cloneDruidDataSource();
		String jdbcUrl = schemaInfo.getJdbcUrl();
		dataSource.setUrl(schemaInfo.getJdbcUrl());
		
		try {
			dataSource.init();
			if (LOGGER.isInfoEnabled()) {
				LOGGER.info("create datasource[{}] success.", jdbcUrl);
			}
		} catch (SQLException e) {
			LOGGER.error("动态创建数据源错误，jdbc_url=[{}]。", jdbcUrl, e);
			return false;
		}
		
		// 手动生成SqlSessionFactory
		SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
		factoryBean.setDataSource(dataSource);
		factoryBean.setConfigLocation(skmbwSqlSessionFactory.getConfigLocation());
		factoryBean.setMapperLocations(skmbwSqlSessionFactory.getMapperLocations());
		
		SqlSessionFactory sqlSessionFactory = null;
		try {
			factoryBean.afterPropertiesSet();
			sqlSessionFactory = factoryBean.getObject();
			if (LOGGER.isInfoEnabled()) {
				LOGGER.info("create MyBatis SqlSessionFactory[{}] success.", jdbcUrl);
			}
		} catch (Exception e) {
			LOGGER.error("动态创建SqlSessionFactory错误，jdbc_url=[{}]。", jdbcUrl, e);
			return false;
		}
		// 添加事务管理器
		DataSourceTransactionManager dsTransactionManager = new DataSourceTransactionManager(dataSource);
		chainedTransactionManager.addTransactionManager(schemaInfo.getSchemaName(), dsTransactionManager);
		
		// 添加SqlSessionFactory
		Map<String, SqlSessionFactory> maps = sqlSessionTemplateProxy.getProxySqlSessionFactory();
		maps.put(schemaInfo.getSchemaName(), sqlSessionFactory);
		if (LOGGER.isInfoEnabled()) {
			LOGGER.info("add MyBatis SqlSessionFactory to App instance success.");
		}
		SchemaHolder.add(schemaInfo.getSchemaName(), schemaInfo);
		return true;
	}
	
	/**
	 * 动态移除某一应用的分区schema数据源
	 * @param schemaInfo 移除条件
	 * @return true成功，false失败
	 */
	public boolean removeSchema(SchemaInfo schemaInfo) {
		Map<String, SqlSessionFactory> maps = sqlSessionTemplateProxy.getProxySqlSessionFactory();
		
		String schema = schemaInfo.getSchemaName();
		SqlSessionFactory sqlSessionFactory = maps.remove(schema);// 移除对应的SqlSessionFactory
		
		DruidDataSource ds = (DruidDataSource) sqlSessionFactory.getConfiguration().getEnvironment().getDataSource();
		ds.close();
		if (LOGGER.isInfoEnabled()) {
			LOGGER.info("close DruidDataSource from App instance success.");
		}
		sqlSessionFactory = null;
		
		chainedTransactionManager.removeTransactionManager(schema);
		if (LOGGER.isInfoEnabled()) {
			LOGGER.info("remove MyBatis TransactionManager from App instance success.");
		}
		SchemaHolder.remove(schemaInfo.getSchemaName());
		return true;
	}
	
	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.notNull(sqlSessionTemplateProxy, "cannot find SqlSessionTemplate's instance.");
		Assert.notNull(chainedTransactionManager, "cannot find MultiDataSourceTransactionManager's instance.");
	}

	public void setSqlSessionTemplateProxy(SqlSessionTemplate sqlSessionTemplateProxy) {
		this.sqlSessionTemplateProxy = sqlSessionTemplateProxy;
	}

	public void setChainedTransactionManager(MultiDataSourceTransactionManager chainedTransactionManager) {
		this.chainedTransactionManager = chainedTransactionManager;
	}

}
